;  PROGRAM: ZMO-CP05.Z80
;  AUTHOR:  Bruce Dudley
;  VERSION: 1.5
;  DATE:  19 Sept, 1988
;-----------------------------------------------------------------------
;	This overlay is set up for an Amstrad CPC6128 under CPM Plus,
;	with a Zenith Z19/Z29 terminal emulation and a Z80SIO and 8253
;	counter.
;-----------------------------------------------------------------------
; 89/04/12 - Modified to ZMP v1.5		- George Conover
; 89/03/15 - Removed the beginning remarks to make the file
;            smaller. If you need the remarks there are in the
;            file ZMP-BLNK.Z80 			- George Conover
; 89/03/14 - Modified to ZMP v1.4		- George Conover
; 88/10/12 - Modified to ZMP v1.3		- Ron Murray
; 88/09/15 - Modified to ZMP v1.2		- Ron Murray
; 88/08/28 - First version of this file		- Bruce Dudley
;
;	Written by -
;	  Ron Murray, c/o Z-Node 62, 061-9-450-0200, Perth, Western Australia.
;-----------------------------------------------------------------------
;
NO	EQU	0
YES	EQU	NOT NO
;
; User-set variables
;
CLKSPD	EQU	4		; Processor clock speed in MHz
;
DEBUG	EQU	NO

;Set the following two equates to the drive and user area which will contain
;   ZMP's .OVR files, .CFG file, .FON file and .HLP file. Set both to zero
;   (null) to locate them on the drive from which ZMP was invoked.

OVERDRIVE	EQU	'A'	; Drive to find overlay files on ('A'-'P')
OVERUSER	EQU	0	; User area to find files

;------------------------------------------------------------------------------
;
; NOT user-set variables
;
USERDEF	EQU	0145H		; Origin of this overlay.  This address
;				;   may change with subsequent revisions
MSPEED	EQU	05CH		; Location of current baud rate.
OVSIZE	EQU	0400H		; Max size of this overlay
;
	.Z80			; Use z80 code
	ASEG			; Absolute
;
	 IF	DEBUG
	ORG	100H		; So you can debug it with CEBUG, ZSID,
;				;   etc.
	 ELSE
	ORG	USERDEF
	 ENDIF			; DEBUG
;
ESC	EQU	1BH
CTRLQ	EQU	11H
CR	EQU	0DH
LF	EQU	0AH
BDOS	EQU	5
;
; Amstrad CPC6128 specific equates
;
MDATA	EQU	0FADCH		; Data port
MSTAT	EQU	0FADDH		; Status port
RDA	EQU	0		; Received data available
TBE	EQU	2		; Tx ready
ONHOOK	EQU	7FH
ONLINE	EQU	80H
ERROR	EQU	70H
BREAK	EQU	0F8H
;
; 8253 Counter equates
;
CTCONT	EQU	0FBDFH		; Control register of the 8253
CTC0	EQU	0FBDCH		; Counter # 0 of the 8253
CTC1	EQU	0FBDDH		; Counter # 1 of the 8253
;
;-----------------------------------------------------------------------
;
; Main code starts here
;
;Jump table for the overlay: do NOT change this
;
CODEBGN	EQU	$
;
JUMPTAB:
	JP	SCRNPR		; Screen print
	JP	MRD		; Modem read with timeout
	JP	MCHIN		; Get a character from modem
	JP	MCHOUT		; Send a character to the modem
	JP	MORDY		; Test for tx buffer empty
	JP	MIRDY		; Test for character received
	JP	SNDBRK		; Send break
	JP	CURSADD		; Cursor addressing
	JP	CLS		; Clear screen
	JP	INVON		; Inverse video on
	JP	INVOFF		; Inverse video off
	JP	HIDE		; Hide cursor
	JP	SHOW		; Show cursor
	JP	SAVECU		; Save cursor position
	JP	RESCU		; Restore cursor position
	JP	MINT		; Service modem interrupt
	JP	INVEC		; Initialise interrupt vectors
	JP	DINVEC		; De-initialise interrupt vectors
	JP	MDMERR		; Test uart flags for error
	JP	DTRON		; Turn DTR on
	JP	DTROFF		; Turn DTR OFF
	JP	INIT		; Initialise uart
	JP	WAIT		; Wait seconds
	JP	MSWAIT		; Wait milliseconds
	JP	USERIN		; User-defined entry routine
	JP	USEROUT		; User-defined exit routine
	JP	GETVARS		; get system variables
  	JP	SETPORT		; Set port (0 or 1)
;
; Spare jumps for compatibility with future versions
;
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
;
SPARE:	RET
;
;-----------------------------------------------------------------------
;
; Screen print function
;
SCRNPR:	DS	0
;
; <== Insert your own code here
;
	CALL	PRINT
	DB	'This function not supported.',CR,LF
	DB	0
;
; <== End of your own code
;
	RET
;
; Get a character from the modem: return in HL
;
MCHIN:	PUSH	BC
;
; <== Insert your own code here
;
	LD	BC,MSTAT
;
MCHIN2:	IN	A,(C)		; Check for char waiting
	BIT	RDA,A
	JR	Z,MCHIN2
	LD	BC,MDATA
	IN	A,(C)		; Read the character
;
; <== End of your own code
;
	LD	L,A		; Put in HL
	LD	H,0
	OR	A		; Set/clear Z
	POP	BC
	RET
;
; Send a character to the modem
;
MCHOUT:	LD	HL,2		; Get the character
	ADD	HL,SP
	LD	A,(HL)
;
; <== Insert your own code here
;
	PUSH	BC
	PUSH	AF		; Save the character
	LD	BC,MSTAT
;
MCHOUT2:IN	A,(C)		; Check for UART ready
	BIT	TBE,A
	JR	Z,MCHOUT2
	POP	AF		; Character in a
	LD	BC,MDATA
	OUT	(C),A		; Send it
	POP	BC
;
; <== End of your own code
;
	RET			; Done
;
; Test for output ready: return YES (1) in HL if ok
;
MORDY:	DS	0
;
; <== Insert your own code here
;
	PUSH	BC
	LD	BC,MSTAT
	LD	HL,0
	IN	A,(C)
	BIT	TBE,A		; Transmit buffer empty
	JR	Z,MORDY1
	INC	HL
;
MORDY1:	DS	0
;
; <== End of your own code
;
	POP	BC
	LD	A,L		; Set/clear Z
	OR	A
	RET
;
;-----------------------------------------------------------------------
;
; Test for character at modem: return YES (1) in HL if so
;
MIRDY:	DS	0
;
; <== Insert your own code here
;
	PUSH	BC
	LD	BC,MSTAT
	LD	HL,0
	IN	A,(C)
	BIT	RDA,A		; Received data available
	JR	Z,MIRDY1
	INC	HL
;
MIRDY1:	DS	0
;
; <== End of your own code
;
	POP	BC
	LD	A,L		; Set/clear Z
	OR	A
	RET
;
;Send a break to the modem: leave empty if your system can't do it
;
SNDBRK:	DS	0
;
; <== Insert your own code here
;
	PUSH	BC
	LD	BC,MSTAT
	LD	A,5
	OUT	(C),A
	LD	A,BREAK		; F8h
	OUT	(C),A
	LD	HL,300		; A 300 mS break
	CALL	WAITHLMS
	LD	A,5
	OUT	(C),A
	LD	A,ONLINE	; 68h
	OUT	(C),A
	LD	A,3
	OUT	(C),A
	LD	A,0E1H		; E1h
	OUT	(C),A
	POP	BC
;
; <== End of your own code
;
	RET
;
; Test UART flags for error: return YES (1) in HL if error.
;
MDMERR:	DS	0
;
; <== Insert your own code here
;
	PUSH	BC
	LD	BC,MSTAT
	LD	HL,0
	IN	A,(C)
	AND	A,ERROR
	JR	Z,MDMER2
	INC	HL
;
MDMER2:	DS	0
;
; <== End of your own code
;
	POP	BC
	LD	A,L		; Set/clear Z
	OR	A
	RET
;
; Turn DTR ON
;
DTRON:	DS	0
;
; <== Insert your own code here
;
	PUSH	BC
	LD	BC,MSTAT
	LD	A,5
	OUT	(C),A
	LD	A,(COMBYT)	; Get the one we used last time
	OR	A,ONLINE	; 80h - set bit 7 high
	LD	(COMBYT),A	; Save it for next time
	OUT	(C),A		; Go
	POP	BC		;
;
; <== End of your own code
;
	RET
;
; Turn DTR OFF
;
DTROFF:	DS	0
;
; <== Insert your own code here
;
	PUSH	BC
	LD	BC,MSTAT
	LD	A,5
	OUT	(C),A
	LD	A,(COMBYT)	; Get the one we used last time
	AND	ONHOOK		; 7Fh - clear bit 7
	LD	(COMBYT),A	; Save it for next time
	OUT	(C),A		; Go
	POP	BC
;
; <== End of your own code
;
	RET
;
; Initialise the SIO +++
; The SIO is set up in four steps:
;
;	1)  Reset
;	2)  Reg 4 - clock, stop bits, parity
;	3)  Reg 5 - dtr, Tx bits, Brk, TxEn, rts
;	4)  Reg 3 - Rx bits, RxEn
;
INIT:	LD	HL,2		; Get parameters
	ADD	HL,SP
	EX	DE,HL
	CALL	GETPARM		; In HL
	LD	(BRATE),HL	; Baud rat
	CALL	GETPARM
	LD	(PARITY),HL	; Parity
	CALL	GETPARM
	LD	(DATA),HL	; Data bits
	CALL	GETPARM
	LD	(STOP),HL	; Stop bits
;
; <== Insert your own code here, using values below
;
	PUSH	BC
	LD	BC,MSTAT
	LD	A,0
	OUT	(C),A		; Point to reg 0
	LD	A,18H		; Reset
	OUT	(C),A		; *** step 1
	LD	A,4		; Point to wrt reg 4
	OUT	(C),A
	LD	E,44H		; Assume  x16, 1 stop, No parity
	LD	A,(STOP)	; Set stop bits
	CP	2		; Set 2 if required
	JR	NZ,SETPAR
	LD	A,08H		; 0000 1000
	OR	E
	LD	E,A
;
SETPAR:	LD	A,(PARITY)	; Set parity bits
	CP	'O'
	JR	NZ,SETPA2
	LD	D,01H		; 0000 0001	ODD
	JR	SETPA3
;
SETPA2:	CP	'E'
	JR	NZ,SETPA4
	LD	D,03H		; 0000 0011	EVEN
;
SETPA3:	LD	A,E
	OR	A,D
	LD	E,A
;
SETPA4:	LD	A,E
	OUT	(C),A		; *** step 2
	LD	A,5		; Point to wrt reg 5 - dtr, Tx bits, etc
	OUT	(C),A
	LD	E,0EAH		; Assume dtr, TX 8 bits, TxEn, rts
	LD	A,(DATA)
	CP	7
	JR	NZ,SETBI2
	LD	D,0BFH		; 1011 1111	7 bits
	LD	A,E
	AND	A,D
	LD	E,A
;
SETBI2:	LD	A,E
	OUT	(C),A		; *** step3
	LD	A,3		; Point to wrt reg 3
	OUT	(C),A
	LD	E,0C1H		; Assume 8 bits
	LD	A,(DATA)
	CP	7
	JR	NZ,SETBI3
	LD	D,07FH		; 0111 1111	7 bits
	LD	A,E
	AND	A,D
	LD	E,A
;
SETBI3:	LD	A,E
	OUT	(C),A		; *** step 4
;
SETBRATE:
	PUSH	IX		; Save IX
	LD	DE,(BRATE)	; Get baud rate value (0-10)
	LD	IX,BRVAL
	ADD	IX,DE
	ADD	IX,DE		; IX now points to the 2-byte value
	LD	A,(IX)		; If zero,
	OR	(IX+1)
	JR	Z,SETBRX	; It's not valid
	LD	BC,CTCONT
	LD	A,36H
	OUT	(C),A
	LD	BC,CTC0
	LD	A,(IX)
	OUT	(C),A		; Low byte
	LD	A,(IX+1)
	OUT	(C),A		; High byte
	LD	BC,CTCONT
	LD	A,76H
	OUT	(C),A
	LD	BC,CTC1
	LD	A,(IX)
	OUT	(C),A		; Low byte
	LD	A,(IX+1)
	OUT	(C),A		; High byte
	LD	A,(BRATE)	; Tell ZMP it's valid
	LD	(MSPEED),A
;
SETBRX:	POP	IX
	POP	BC
;
; <== End of your own code
;
	RET
;
BRATE:	DW	5		; Baud rate
PARITY:	DW	'N'		; Parity
DATA:	DW	8		; Data bits
STOP:	DW	1		; Stop bits
COMBYT:	DB	0EAH		; Save here
;
; Values for 8253 control reg for each baud rate: 0 if invalid
;
BRVAL:	DW	470H		; 110		 0
	DW	1A1H		; 300		 1
	DW	116H		; 450		 2
	DW	0D0H		; 600		 3
	DW	0B0H		; 710		 4
	DW	068H		; 1200		 5
	DW	034H		; 2400		 6
	DW	01AH		; 4800		 7
	DW	00DH		; 9600		 8
	DW	007H		; 19200 	 9
	DW	0		; 38400 	10
	DW	0		; 57600 	11
	DW	0		; 76800 	12
	DW	0		; 115200	13
;
; Set the port. ZMP supplies either 0 or 1 as a parameter.
;
setport:
	ld	hl,2		; get port number
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL (values are 0 and 1)

				; <== Insert your own code here

				; <== End of your own code
	ret
;
;-----------------------------------------------------------------------
;
; Video terminal sequences: these are for Zenith Z19/Z29: Modify as you
; wish.
;
; Cursor addressing
;
CURSADD:LD	HL,2		; Get parameters
	ADD	HL,SP
	EX	DE,HL
	CALL	GETPARM		; In HL
	LD	(ROW),HL	; Row
	CALL	GETPARM
	LD	(COL),HL	; Column
;
; <== Insert your own code here, using values in row and column
;
	CALL	PRINT
	DB	ESC,'Y',0	; Leadin
	LD	A,(ROW)		; Row first
	ADD	A,' '		; Add offset
	CALL	COUT
	LD	A,(COL)		; Sane for column
	ADD	A,' '
	CALL	COUT
;
; <== End of your own code
;
	RET
;
ROW:	DS	2		; Row
COL:	DS	2		; Column
;
; Clear screen
;
CLS:	CALL	PRINT
	DB	ESC,'E',ESC,'H',0
	RET
;
; Inverse video on
;
INVON:	CALL	PRINT
	DB	ESC,'p',0
	RET
;
; Inverse video off
;
INVOFF:	CALL	PRINT
	DB	ESC,'q',0
	RET
;
; Turn off cursor
;
HIDE:	CALL	PRINT
	DB	ESC,'f',0
	RET
;
; Turn on cursor
;
SHOW:	CALL	PRINT
	DB	ESC,'e',0
	RET
;
; Save cursor position
;
SAVECU:	CALL	PRINT
	DB	ESC,'j',0
	RET
;
; Restore cursor position
;
RESCU:	CALL	PRINT
	DB	ESC,'k',0
	RET
;
;-----------------------------------------------------------------------
;
; Service modem interrupt
;
MINT:	RET
;
; Initialise interrupt vectors
;
INVEC:	RET
;
; De-initialise interrupt vectors
;
DINVEC:	RET
;
; User-defined entry routine: leave empty if not used
;
USERIN:	RET
;
; User-defined exit routine: leave empty if not used
;
USEROUT:RET
;
;------------------- End of user-defined code --------------------------
;		 Do not change anything below here
;
; Modem character test for 100 ms
;
MRD:	PUSH	BC		; Save BC
	LD	BC,100		; Set limit
;
MRD1:	CALL	MIRDY		; Char at modem?
	JR	NZ,MRD2		; Yes, exit
	LD	HL,1		; Else wait 1 ms
	CALL	WAITHLMS
	DEC	BC		; Loop till done
	LD	A,B
	OR	C
	JR	NZ,MRD1
	LD	HL,0		; None there, result=0
	XOR	A
;
MRD2:	POP	BC
	RET
;
; Inline print routine: destroys A and HL
;
PRINT:	EX	(SP),HL		; Get address of string
;
PLOOP:	LD	A,(HL)		; Get next
	INC	HL		; Bump pointer
	OR	A		; Done if zero
	JR	Z,PDONE
	CALL	COUT		; Else print
	JR	PLOOP		; And loop
;
PDONE:	EX	(SP),HL		; Restore return address
	RET			; And quit
;
; Output a character in A to the console
;
COUT:	PUSH	BC		; Save registers
	PUSH	DE
	PUSH	HL
	LD	E,A		; Character to E
	LD	C,2
	CALL	BDOS		; Print it
	POP	HL
	POP	DE
	POP	BC
	RET
;
; Wait(seconds)
;
WAIT:	LD	HL,2
	ADD	HL,SP
	EX	DE,HL		; Get delay size
	CALL	GETPARM
;				; Fall thru to...
; Wait seconds in HL
;
WAITHLS:PUSH	BC		; Save BC
	PUSH	DE		; DE
	PUSH	IX		; And IX
	LD	IX,0		; Then point IX to 0
;				;   so we don't upset memory-mapped I/O
;
; Calculate values for loop constants. Need to have two loops to avoid
; 16-bit overflow with clock speeds above 9 MHz.
;
OUTERVAL EQU	(CLKSPD	/ 10) +	1
;
INNERVAL EQU	(6667 /	OUTERVAL) * CLKSPD
;
WAIT10:	LD	B,OUTERVAL
;
WAIT11:	LD	DE,INNERVAL
;
WAIT12:	BIT	0,(IX)		; Time-wasters
	BIT	0,(IX)
	BIT	0,(IX)		; 20 T-states each
	BIT	0,(IX)
	BIT	0,(IX)
	BIT	0,(IX)
	DEC	DE
	LD	A,E
	LD	A,D
	OR	E
	JR	NZ,WAIT12	; 150 T-states per inner loop
	DJNZ	WAIT11		; Decrement outer loop
	DEC	HL		; Ok, decrement count in HL
	LD	A,H
	OR	L
	JR	NZ,WAIT10
	POP	IX		; Done -- restore IX
	POP	DE		; DE
	POP	BC		; And BC
	RET
;
; Wait milliseconds
;
MSWAIT:	LD	HL,2
	ADD	HL,SP
	EX	DE,HL		; Get delay size
	CALL	GETPARM
;
; Wait milliseconds in HL
;
WAITHLMS:
	PUSH	DE
;
W1MS0:	LD	DE,39 *	CLKSPD
;
W1MS1:	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,W1MS1
	DEC	HL
	LD	A,H
	OR	L
	JR	NZ,W1MS0
	POP	DE
	RET
;
; Get next parameter from (DE) into HL
;
GETPARM:EX	DE,HL		; Get address into HL
	LD	E,(HL)		; Get low
	INC	HL
	LD	D,(HL)		; Then hihi
	INC	HL		; Bump for next
	EX	DE,HL		; Result in HL, address still in DE
	RET
;
;Get address of user-defined variables
;
GETVARS:
	LD	HL,USERVARS
	RET
USERVARS:
	DW	OVERDRIVE	; .OVR etc. drive/user
	DW	OVERUSER
;
	 IF	($ - CODEBGN) GT OVSIZE
TOOBIG:	JP	ERRVAL		; Overlay is too large
	 ENDIF
;
	END
